home *** CD-ROM | disk | FTP | other *** search
- From: wietse@wzv.win.tue.nl (Wietse Venema)
- Newsgroups: comp.unix.programmer,comp.security.misc
- Subject: Re: proposal to improve set-uid security
- Message-ID: <3348@wzv.win.tue.nl>
- Date: 27 May 92 20:29:02 GMT
- Organization: Eindhoven University of Technology, The Netherlands
-
- This article was inspired by a recently-discovered security problem
- with environment variables that control the use of SunOS shared
- libraries (see comp.security.announce for the relevant CERT advisory).
-
- The problem is not specific to the SunOS environment, though. There is
- a basic flaw in the way that most set-xid commands try to circumvent
- trojan horses. This article discusses a possible solution.
-
- Upon creation, a UNIX process inherits the full environment from its
- predecessor. In the case of set-uid (set-gid) commands, it is up to
- the process to sanitize its environment before running other commands.
- Cleanup is usually limited to a few environment variables such as PATH
- or perhaps IFS, and the remainder of the environment is just passed
- on. The problem is: environment information is considered innocent
- until proven guilty.
-
- The introduction of user-selectable shared libraries is just an example
- of side effects caused by unanticipated environment information. These
- and similar unexpected side effects complicate the design of reliable
- set-xid commands, because the author of a program cannot possibly know
- about all the documented (and undocumented!) environment variables that
- may be exploited with present and future UNIX implementations.
-
- Censoring of specific environment variables is not a general solution,
- especially when done in user-level code: the "black list" would never
- be complete. Selective inheritance of the environment may be a better
- alternative.
-
- My proposal is a single user-level function called newenv(). Its
- purpose is to create a new environment that is populated only by
- explicitly-named environment variables. The interface is:
-
- int newenv(char *arg1, ..., char *argn, (char *) 0)
-
- If an argument is of the form "name", the corresponding entry from
- the old environment is linked to the new environment.
-
- If an argument is of the form "name=value", the string itself is
- linked to the new environment.
-
- Thus, instead of censoring specific environment variables that at some
- point in time were proven "guilty" of abuse, a process retains only an
- explicitly-enumerated subset of its original environment. The latter
- approach should be much more effective against trojan horse attacks
- than just fixing IFS, PATH and a few others.
-
- Example: many set-uid commands would work fine with something like:
-
- if (newenv("PATH=/bin:/usr/bin:/usr/ucb", (char *) 0)) {
- perror("newenv");
- exit(1);
- }
-
- No original environment information would be retained at all, which
- eliminates the risk of trojan horses in the process environment.
-
- Some set-xid commands commands require that HOME, NAME or ORGANIZATION
- be preserved; that could be achieved with:
-
- newenv("PATH=/bin:/usr/bin:/usr/ucb", "HOME",
- "NAME", "ORGANIZATION", (char *) 0)
-
- The proposal allows for a simple and easy to verify implementation.
- Attached are a sample implementation and a manual page that gives a
- more accurate description than the informal discussion above.
-
- Wietse
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of shell archive."
- # Contents: newenv.c newenv.3 Makefile
- # Wrapped by wietse@wzv on Wed May 27 22:27:47 1992
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f newenv.c -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"newenv.c\"
- else
- echo shar: Extracting \"newenv.c\" \(2277 characters\)
- sed "s/^X//" >newenv.c <<'END_OF_newenv.c'
- X /*
- X * newenv - sanitize process environment
- X *
- X * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
- X * Computing Science, Eindhoven University of Technology, The Netherlands.
- X */
- X
- X/* C library */
- X
- X#ifdef __STDC__
- X#include <string.h>
- X#include <stdlib.h>
- X#else
- Xextern char *strchr();
- X#endif
- X
- X/* Attempt to unify the stdarg and varargs interfaces */
- X
- X#ifdef __STDC__
- X#include <stdarg.h>
- X#define VARARGS(func,type,arg) func(type arg, ...)
- X#define VASTART(ap,type,name) va_start(ap, name)
- X#else
- X#include <varargs.h>
- X#define VARARGS(func,type,arg) func(va_alist) va_dcl
- X#define VASTART(ap,type,name) type name; va_start(ap); name = va_arg(ap, type)
- X#endif
- X
- X/* findenv - look up environment entry */
- X
- Xstatic char *findenv(env, name)
- Xchar **env;
- Xregister char *name;
- X{
- X register char *cp;
- X register int len = strlen(name);
- X register char **cpp;
- X
- X#define STREQ(x,y,l) (x[0] == y[0] && strncmp(x,y,l) == 0)
- X
- X for (cpp = env; cp = *cpp; cpp++)
- X if (STREQ(name, cp, len) && cp[len] == '=')
- X return (cp);
- X return (0);
- X}
- X
- X/* newenv - set up limited environment */
- X
- Xint VARARGS(newenv, char *, head)
- X{
- X va_list ap;
- X register char *item;
- X register char **old;
- X static char *new[] = {0};
- X extern char **environ;
- X
- X VASTART(ap, char *, head);
- X
- X /*
- X * Move original environment away and install an empty one. In principle,
- X * this code should work even when we are called more than once. In
- X * practice, most putenv() implementations maintain internal state and
- X * have problems when the environment changes without their consent.
- X */
- X
- X old = environ;
- X environ = new;
- X
- X /*
- X * Fill in the new environment. If "name=value" is given it is linked to
- X * the new environment; if "name" is given its old environment entry is
- X * linked to the new environment.
- X */
- X
- X for (item = head; item; item = va_arg(ap, char *))
- X if (strchr(item, '=') || (item = findenv(old, item)))
- X if (putenv(item))
- X return (-1);
- X return (0);
- X}
- X
- X#ifdef TEST
- X
- Xmain()
- X{
- X int ret;
- X
- X if (ret = newenv("PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin",
- X "HOME",
- X "NAME",
- X "ORGANIZATION",
- X (char *) 0))
- X perror("newenv");
- X
- X system("printenv");
- X return (ret);
- X}
- X
- X#endif
- END_OF_newenv.c
- if test 2277 -ne `wc -c <newenv.c`; then
- echo shar: \"newenv.c\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f newenv.3 -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"newenv.3\"
- else
- echo shar: Extracting \"newenv.3\" \(2423 characters\)
- sed "s/^X//" >newenv.3 <<'END_OF_newenv.3'
- X.TH NEWENV 3
- X.SH NAME
- Xnewenv \- set up restricted environment
- X.SH SYNOPSIS
- X.na
- X.nf
- Xint newenv([char *arg1, ..., char *argn, ] (char *) 0)
- X.ad
- X.fi
- X.SH DESCRIPTION
- Xnewenv() sets up a new, restricted, environment. Its primary purpose is
- Xto protect privileged commands against trojan horse attacks via
- Xunexpected environment variables: newenv() retains only an explicitly
- Xnamed subset of the process environment.
- X.PP
- XAfter creating an empty environment, newenv() processes its argument
- Xlist. Arguments can assume the following forms:
- X.TP
- X"name"
- X.br
- XThe corresponding entry from the old environment is linked to
- Xthe new environment. A request for a non-existing entry has no effect.
- X.TP
- X"name=value"
- X.br
- XThe "name=value" information is linked to the new
- Xenvironment.
- X.PP
- XThe resulting environment can be accessed in the usual manner with the
- Xgetenv() and putenv() library functions.
- X.SH EXAMPLES
- XA typical set-uid (set-gid) command would do:
- X.sp
- X.in +2
- X.nf
- X.na
- Xif (newenv("PATH=/bin:/usr/bin:/usr/ucb", (char *) 0)) {
- X perror("newenv");
- X exit(1);
- X}
- X.fi
- X.ad
- X.sp
- X.PP
- XIn this example, no information is retained from the original
- Xenvironment. It should take care of all trojans that involve
- Xenvironment variables.
- X.PP
- XSome commands require that the NAME, HOME or ORGANIZATION environment
- Xvariables be preserved, too:
- X.sp
- X.in +2
- X.nf
- X.na
- Xnewenv("PATH=/bin:/usr/bin:/usr/ucb",
- X "NAME", "HOME", "ORGANIZATION", (char *) 0)
- X.fi
- X.ad
- X.PP
- Xwould take care of that.
- X.SH SEE ALSO
- Xputenv(3), getenv(3)
- X.SH DIAGNOSTICS
- Xnewenv() returns a non-zero value in case of memory-allocation
- Xproblems. Under these conditions, the resulting environment will be
- Xonly a subset of what was requested.
- X.SH WARNINGS
- Xnewenv() uses putenv(), so the usual caveats apply: the third argument
- Xto main() is not modified; "name=value" arguments should not rely on
- Xautomatic storage that may be reused before the process terminates.
- X.sp
- XMany putenv() implementations keep internal state and have problems
- Xwhen the environment changes between putenv() calls. For this reason,
- Xnewenv() calls should not be preceded by any putenv() or newenv()
- Xcalls.
- X.sp
- XSome programs may misbehave when commonly-used environment variables
- Xsuch as TERM, HOME and SHELL are missing from the environment.
- X.SH AUTHOR(S)
- X.na
- X.nf
- XWietse Venema
- XEindhoven University of Technology
- XDepartment of Mathematics and Computer Science
- XDen Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
- END_OF_newenv.3
- if test 2423 -ne `wc -c <newenv.3`; then
- echo shar: \"newenv.3\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f Makefile -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"Makefile\"
- else
- echo shar: Extracting \"Makefile\" \(129 characters\)
- sed "s/^X//" >Makefile <<'END_OF_Makefile'
- XCFLAGS = -g
- X
- Xdemo: newenv
- X ./newenv
- X
- Xnewenv: newenv.c
- X $(CC) $(CFLAGS) -DTEST -o $@ newenv.c
- X
- Xclean:
- X rm -f newenv newenv.o core
- END_OF_Makefile
- if test 129 -ne `wc -c <Makefile`; then
- echo shar: \"Makefile\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- echo shar: End of shell archive.
- exit 0
-
-